using System;
using System.Collections.Generic;
using Oracle.ManagedDataAccess.Client;
using OracleInternal.Common;
using OracleInternal.ServiceObjects;

namespace OracleInternal.ConnectionPool
{
	internal class Pool<PM, CP, PR> where PM : PoolManager<PM, CP, PR>, new()where CP : Pool<PM, CP, PR>, new()where PR : PoolResource<PM, CP, PR>, new()
	{
		public PM m_pm;

		public SyncQueueList<PR> m_cpListPR;

		public SyncQueueList<PR> m_cpQueuePR;

		public string m_instanceName;

		public string m_serviceName;

		public DateTime m_lastHADownEventUtcDateTime;

		public bool m_bInstanceDown;

		internal bool HasValidConnections => m_cpListPR.Count > 0;

		public Pool()
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.Pool, OracleTraceFuncName.ctor);
			}
			try
			{
				m_cpListPR = new SyncQueueList<PR>();
				m_cpQueuePR = new SyncQueueList<PR>();
				m_lastHADownEventUtcDateTime = DateTime.MinValue;
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.Pool, OracleTraceFuncName.ctor, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.Pool, OracleTraceFuncName.ctor);
				}
			}
		}

		public virtual void PutNewPR(PR pr, bool bForPoolPopulation)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.Pool, OracleTraceFuncName.PutNewPR);
			}
			try
			{
				bool flag = false;
				if (m_bInstanceDown)
				{
					if (pr.m_creationTime.ToUniversalTime() > pr.m_cp.m_lastHADownEventUtcDateTime.ToUniversalTime())
					{
						flag = true;
						m_bInstanceDown = false;
						if (pr.ServiceName != null)
						{
							ServiceCtx serviceCtx = m_pm.m_dictSvcCtx[pr.ServiceName];
							if (serviceCtx != null && serviceCtx.m_databaseName != null && pr.m_databaseName != null && serviceCtx.m_databaseName.ToLowerInvariant() != pr.m_databaseName.ToLowerInvariant())
							{
								serviceCtx.m_databaseName = pr.m_databaseName;
							}
						}
					}
					if (ProviderConfig.m_bTraceLevelPrivate)
					{
						string text = $"[pm_id={m_pm.m_id}][instance={m_instanceName}][instance_down={m_bInstanceDown}][type=new_connection][state_changed={flag}][con_creation_time_utc={pr.m_creationTime.ToUniversalTime().ToString()}][last_down_event_utc={m_lastHADownEventUtcDateTime.ToString()}]";
						Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.Pool, OracleTraceFuncName.PutNewPR, text);
					}
				}
				m_cpListPR.Add(pr);
				if (bForPoolPopulation)
				{
					if (pr.m_pm != null && pr.m_pm.m_cs != null && DrcpType.True == pr.m_pm.m_cs.m_drcpEnabled && pr.bDRCPServerProcessAttached)
					{
						pr.DetachServerProcess(null, bUseDRCPMultiTag: false);
						pr.bDRCPServerProcessAttached = false;
						pr.bGotMatchingServerProcess = false;
					}
					m_cpQueuePR.Add(pr);
					if (ProviderConfig.m_bTraceLevelPrivate)
					{
						Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.Pool, OracleTraceFuncName.PutNewPR, Trace.GetCPInfo(pr as OracleConnectionImpl, null, null, "cp:put:reg1"));
					}
				}
				pr.m_cp = (CP)this;
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.Pool, OracleTraceFuncName.PutNewPR, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.Pool, OracleTraceFuncName.PutNewPR);
				}
			}
		}

		public virtual void Put(PR pr)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.Pool, OracleTraceFuncName.Put);
			}
			try
			{
				if (pr.m_mtsTxnCtx != null)
				{
					try
					{
						(pr as OracleConnectionImpl).ResetMTSTxnCtx();
					}
					catch
					{
					}
				}
				m_cpQueuePR.Enqueue(pr);
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.Pool, OracleTraceFuncName.Put, Trace.GetCPInfo(pr as OracleConnectionImpl, null, null, "cp:put:reg2"));
				}
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.Pool, OracleTraceFuncName.Put, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.Pool, OracleTraceFuncName.Put);
				}
			}
		}

		internal static void MatchCriteria(PR pr, ref PR bestMatchingPR, ref uint bitRepForBestMatchPR, OracleConnection connRefForCriteria, bool prForDTXN)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.Pool, OracleTraceFuncName.MatchCriteria);
			}
			uint num = 0u;
			uint num2 = 268435456u;
			bool flag = true;
			try
			{
				if (pr == null)
				{
					return;
				}
				connRefForCriteria.m_bfoundPRMatchingAllCrit = true;
				for (int i = 0; i < PoolManager<OraclePoolManager, OraclePool, OracleConnectionImpl>.m_criteriaToCritTypeArray.Length; i++)
				{
					uint num3 = pr.m_criteriaIds[i];
					uint num4 = connRefForCriteria.m_criteriaIds[i];
					if (num3 == num4)
					{
						num |= num2;
					}
					else
					{
						connRefForCriteria.m_bfoundPRMatchingAllCrit = false;
						if (((!prForDTXN) ? PoolManager<OraclePoolManager, OraclePool, OracleConnectionImpl>.m_criteriaToCritTypeArray[i] : PoolManager<OraclePoolManager, OraclePool, OracleConnectionImpl>.m_criteriaToCritTypeForDTXN[i]) == 0)
						{
							flag = false;
							return;
						}
					}
					num2 >>= 1;
				}
				if (num > bitRepForBestMatchPR || connRefForCriteria.m_bfoundPRMatchingAllCrit || bestMatchingPR == null)
				{
					bestMatchingPR = pr;
					bitRepForBestMatchPR = num;
					connRefForCriteria.m_bBestMatchPRHasAllMustCrit = (flag ? true : false);
				}
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.Pool, OracleTraceFuncName.MatchCriteria, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.Pool, OracleTraceFuncName.MatchCriteria);
				}
			}
		}

		internal void GetMatchingPR(ref PR bestMatchingPR, OracleConnection connRefForCriteria)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.Pool, OracleTraceFuncName.GetMatchingPR);
			}
			try
			{
				uint bitRepForBestMatchPR = 0u;
				lock (m_cpQueuePR.m_sync)
				{
					for (int i = 0; i < m_cpQueuePR.m_list.Count; i++)
					{
						MatchCriteria(m_cpQueuePR.m_list[i], ref bestMatchingPR, ref bitRepForBestMatchPR, connRefForCriteria, prForDTXN: false);
						if (connRefForCriteria.m_bfoundPRMatchingAllCrit)
						{
							break;
						}
					}
					if (bestMatchingPR != null)
					{
						if (connRefForCriteria == null || connRefForCriteria.CanReturnBestMatchingPR())
						{
							m_cpQueuePR.m_list.Remove(bestMatchingPR);
						}
						else
						{
							bestMatchingPR = null;
						}
					}
				}
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.Pool, OracleTraceFuncName.GetMatchingPR, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.Pool, OracleTraceFuncName.GetMatchingPR);
				}
			}
		}

		public virtual PR Get(OracleConnection connRefForCriteria)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.Pool, OracleTraceFuncName.Get);
			}
			try
			{
				try
				{
					PR bestMatchingPR = null;
					while (bestMatchingPR == null)
					{
						if (connRefForCriteria != null && !connRefForCriteria.m_fromMTS)
						{
							GetMatchingPR(ref bestMatchingPR, connRefForCriteria);
						}
						else
						{
							bestMatchingPR = m_cpQueuePR.Dequeue();
						}
						if (bestMatchingPR == null)
						{
							return null;
						}
						bestMatchingPR.BeginRequest();
						bool flag = false;
						lock (bestMatchingPR)
						{
							if (bestMatchingPR.m_deletionRequestor != 0)
							{
								flag = m_pm.Close(bestMatchingPR, null);
							}
						}
						if (flag)
						{
							bestMatchingPR = null;
						}
					}
					if (bestMatchingPR != null && ProviderConfig.m_bTraceLevelPrivate)
					{
						Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.Pool, OracleTraceFuncName.Get, Trace.GetCPInfo(bestMatchingPR as OracleConnectionImpl, null, null, "get:reg1"));
					}
					return bestMatchingPR;
				}
				catch (Exception)
				{
					return null;
				}
			}
			catch (Exception ex2)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.Pool, OracleTraceFuncName.Get, ex2);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.Pool, OracleTraceFuncName.Get);
				}
			}
		}

		public PR GetProxy(ConnectionString cs, OracleConnection connRefForCriteria)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.Pool, OracleTraceFuncName.GetProxy);
			}
			uint bitRepForBestMatchPR = 0u;
			PR bestMatchingPR = null;
			PR val = null;
			if (m_pm.m_cs.m_drcpEnabled == DrcpType.True && m_pm.m_cs.m_securedPassword != null)
			{
				throw new Exception("ORA-28150: proxy not authorized to connect as client");
			}
			try
			{
				if (connRefForCriteria != null && connRefForCriteria.m_fromMTS)
				{
					connRefForCriteria = null;
				}
				List<PR> list = m_cpQueuePR.GetList();
				lock (m_cpQueuePR.m_sync)
				{
					foreach (PR item in list)
					{
						if (connRefForCriteria != null)
						{
							MatchCriteria(item, ref bestMatchingPR, ref bitRepForBestMatchPR, connRefForCriteria, prForDTXN: false);
						}
						if (!item.m_bEndUserSessionEstablished && m_pm.m_cs.m_drcpEnabled == DrcpType.False)
						{
							if (m_cpQueuePR.m_list.Remove(item))
							{
								if (ProviderConfig.m_bTraceLevelPrivate)
								{
									Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.Pool, OracleTraceFuncName.GetProxy, Trace.GetCPInfo(item as OracleConnectionImpl, null, null, "cp:get:reg1"));
								}
								return item;
							}
						}
						else
						{
							if (!(item.m_cs.m_userId == cs.m_userId) || (item.m_cs != cs && !ConnectionString.SecureStringsEqual(item.m_cs.m_securedPassword, cs.m_securedPassword)))
							{
								continue;
							}
							if (val == null)
							{
								val = item;
							}
							if (connRefForCriteria != null && bestMatchingPR != item)
							{
								continue;
							}
							bestMatchingPR = item;
							if ((connRefForCriteria == null || connRefForCriteria.m_bfoundPRMatchingAllCrit) && m_cpQueuePR.m_list.Remove(bestMatchingPR))
							{
								if (ProviderConfig.m_bTraceLevelPrivate)
								{
									Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.Pool, OracleTraceFuncName.GetProxy, Trace.GetCPInfo(bestMatchingPR as OracleConnectionImpl, null, null, "cp:get:reg1"));
								}
								return bestMatchingPR;
							}
						}
					}
					if (connRefForCriteria != null)
					{
						PR val2 = null;
						if (bestMatchingPR != null && connRefForCriteria.CanReturnBestMatchingPR())
						{
							val2 = bestMatchingPR;
						}
						else if (val != null)
						{
							val2 = val;
						}
						if (m_cpQueuePR.m_list.Remove(val2))
						{
							if (ProviderConfig.m_bTraceLevelPrivate)
							{
								Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.Pool, OracleTraceFuncName.GetProxy, Trace.GetCPInfo(val2 as OracleConnectionImpl, null, null, "cp:get:reg2"));
							}
							return val2;
						}
					}
					list = m_cpQueuePR.GetList();
					foreach (PR item2 in list)
					{
						if (!item2.m_bEndUserSessionEstablished && m_pm.m_cs.m_drcpEnabled == DrcpType.False)
						{
							if (m_cpQueuePR.Remove(item2))
							{
								if (ProviderConfig.m_bTraceLevelPrivate)
								{
									Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.Pool, OracleTraceFuncName.GetProxy, Trace.GetCPInfo(item2 as OracleConnectionImpl, null, null, "cp:get:reg3"));
								}
								return item2;
							}
						}
						else if (m_cpQueuePR.Remove(item2))
						{
							if (ProviderConfig.m_bTraceLevelPrivate)
							{
								Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.Pool, OracleTraceFuncName.GetProxy, Trace.GetCPInfo(item2 as OracleConnectionImpl, null, null, "cp:get:reg4"));
							}
							return item2;
						}
					}
				}
				return null;
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.Pool, OracleTraceFuncName.GetProxy, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.Pool, OracleTraceFuncName.GetProxy);
				}
			}
		}

		public void MarkAllPRsForDeletion(DateTime haEventUtcDataTime, bool isHAEvnt = false)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.Pool, OracleTraceFuncName.MarkAllPRsForDeletion);
			}
			try
			{
				m_lastHADownEventUtcDateTime = haEventUtcDataTime;
				bool flag = false;
				if (!m_bInstanceDown)
				{
					flag = true;
					m_bInstanceDown = true;
					if (ProviderConfig.m_bTraceLevelPrivate)
					{
						string text = $"[pm_id={m_pm.m_id}][instance={m_instanceName}][instance_down={m_bInstanceDown}][state_changed={flag}][down_event_utc={haEventUtcDataTime.ToString()}][last_down_event_utc={m_lastHADownEventUtcDateTime.ToString()}]";
						Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.Pool, OracleTraceFuncName.MarkAllPRsForDeletion, text);
					}
				}
				List<PR> list = m_cpListPR.GetList();
				for (int i = 0; i < list.Count; i++)
				{
					try
					{
						PR val = list[i];
						if (val == null)
						{
							continue;
						}
						lock (val)
						{
							if (!isHAEvnt)
							{
								if (val.m_deletionRequestor != DeletionRequestor.HA)
								{
									if (ProviderConfig.m_bTraceLevelPrivate)
									{
										string str = $"({val.m_endUserSessionId}:{val.m_endUserSerialNum})";
										Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.PoolManager, OracleTraceFuncName.MarkAllPRsForDeletion, str + " : (HA) Setting pr.m_deletionRequestor = DeletionRequestor.ClearPool");
									}
									val.m_deletionRequestor = DeletionRequestor.ClearPool;
								}
							}
							else
							{
								if (ProviderConfig.m_bTraceLevelPrivate)
								{
									string str2 = $"({val.m_endUserSessionId}:{val.m_endUserSerialNum})";
									Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.Pool, OracleTraceFuncName.MarkAllPRsForDeletion, str2 + " : (HA) Setting pr.m_deletionRequestor = DeletionRequestor.HA");
								}
								val.m_deletionRequestor = DeletionRequestor.HA;
							}
						}
					}
					catch
					{
					}
				}
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.Pool, OracleTraceFuncName.MarkAllPRsForDeletion, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.Pool, OracleTraceFuncName.MarkAllPRsForDeletion);
				}
			}
		}

		public void ClearPool(PR prToRetain, bool isHAEvnt = false)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.Pool, OracleTraceFuncName.ClearPool);
			}
			try
			{
				List<PR> list = m_cpListPR.GetList();
				for (int i = 0; i < list.Count; i++)
				{
					if (prToRetain != null && prToRetain == list[i])
					{
						continue;
					}
					try
					{
						PR val = list[i];
						if (val == null)
						{
							continue;
						}
						lock (val)
						{
							if (!isHAEvnt)
							{
								if (val.m_deletionRequestor != DeletionRequestor.HA)
								{
									val.m_deletionRequestor = DeletionRequestor.ClearPool;
								}
							}
							else
							{
								if (ProviderConfig.m_bTraceLevelPrivate)
								{
									string str = $"({val.m_endUserSessionId}:{val.m_endUserSerialNum})";
									Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.Pool, OracleTraceFuncName.ClearPool, str + " : (HA) Setting pr.m_deletionRequestor = DeletionRequestor.HA");
								}
								val.m_deletionRequestor = DeletionRequestor.HA;
							}
							val.m_pm.Close(val, null);
						}
					}
					catch
					{
					}
				}
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.Pool, OracleTraceFuncName.ClearPool, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.Pool, OracleTraceFuncName.ClearPool);
				}
			}
		}

		~Pool()
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.Pool, OracleTraceFuncName.dtor);
			}
			try
			{
				try
				{
					ClearPool(null);
				}
				catch (Exception ex)
				{
					OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.Pool, OracleTraceFuncName.dtor, ex);
					throw;
				}
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.Pool, OracleTraceFuncName.dtor);
				}
			}
		}
	}
}
